[23기_김도현] spring tutorial 미션 제출합니다.#7
[23기_김도현] spring tutorial 미션 제출합니다.#7kimdoes wants to merge 6 commits intoCEOS-Developers:kimdoesfrom
Conversation
src/main/resources/application.yaml
Outdated
| datasource: | ||
| url: jdbc:mysql://localhost:3306/test_db?allowPublicKeyRetrieval=true&useSSL=false&characterEncoding=UTF-8 | ||
| username: root | ||
| password: leseoul2143** |
There was a problem hiding this comment.
p2. 로컬 DB이긴 하지만 직접 패스워드를 노출하는 것은 위험할 수도 있고, 추후 협업시 충돌이 잦을 수 있으니 환경변수 파일을 도입해보시는건 어떨까요? 아래 적용 방법 블로그 남길게요!
| 이 과정을 통해서 스프링은 Bean을 등록하고, 필드에 의존성을 주입한다. 이 과정은 모두 비즈니스 로직이 시작되기 전에 일어나게된다. | ||
|
|
||
| 이 직후에 postProcessAfterInitialization 어노테이션이 호출되어서 @PostConstruct 메서드가 실행되고, 애플리케이션이 종료되면 ApplicationContext도 같이 제거되고 그 때 빈도 같이 제거된다. 만약 @PreDestroy 어노테이션이 적용된 메서드가 있다면 애플리케이션이 끝나기 전에 호출되어 실행된다. | ||
|
|
There was a problem hiding this comment.
컴포넌트 스캔과 빈 생성, DI 주입까지의 과정을 코드와 함께 자세하게 적으셨네요! 수고하셨습니다 저는 하나하나 코드를 뜯어볼 생각은 못했는데 덕분에 유익한 시간이었습니다 ㅎㅎ
| 스프링은 웹 서버의 구동을 더 쉽게 도와주는 프레임워크, 톰캣서버는 스프링을 구동할 수 있는 WAS, 아파치 서버는 요청을 받아서 WAS가 구동되도록하는 웹 서버에 해당한다. | ||
|
|
||
| # CGV DB 모델링 | ||
| https://www.erdcloud.com/d/vXew5ExzkDE48pxDi |
There was a problem hiding this comment.
DB 모델링까지 하시느라 수고하셨습니다~
주어진 요구사항 외에도 세세한 부분까지 신경쓰신 게 느껴졌습니다
AudienceData를 따로 분리해 성별, 연령별 변동성이 큰 데이터들을 관리하여 movie 테이블의 비대화를 막으신 인상깊어요
또한 TheatherFood를 통해 동일한 메뉴를 갖되, 매장별로 품절여부를 관리할 수 있다는 부분도 설계에 잘 반영되어있네요!
저는 아직 데이터 모델링 과제를 하지 않았는데 해당부분 반영해서 진행해야겠습니다 ㅎㅎ
| 스프링은 웹 서버의 구동을 더 쉽게 도와주는 프레임워크, 톰캣서버는 스프링을 구동할 수 있는 WAS, 아파치 서버는 요청을 받아서 WAS가 구동되도록하는 웹 서버에 해당한다. | ||
|
|
||
| # CGV DB 모델링 | ||
| https://www.erdcloud.com/d/vXew5ExzkDE48pxDi |
There was a problem hiding this comment.
다만 몇가지 제안을 드려봅니다
- 영화 예매/취소와 관련된 테이블을 추가해 유저가 어떤 영화를 언제 예매 or 취소했는지, 총 결제 금액은 얼마인지 등의 정보를 제공하는 것이 어떨지 제안드립니다!
- 어떻게 좌석을 연결할 것인지 한 명이 여러개의 좌석을 예매할 수 있는데 이부분을 어떻게 처리할 것인지에 대한 고민도 나누면 좋을거 같습니다
- 극장에는 특별관과 일반관이 있다고 합니다!
- 영화에는 여러개의 리뷰가 달릴 수 있고, 영화에는 여러 명의 배우가 배우는 여러개의 영화에 출연할 수 있으므로 movie와 comment 테이블, movie와 actor 테이블의 연관관계 설정은 각각 1:n, n:m이 더 알맞다는 생각이 듭니다
- TheaterFood - theater 연관관계는 극장음식 쪽에 극장 아이디가 외래키로 설정되어야 할 것 같습니다
- 실제 영화를 볼 때 좌석 번호가 있으니 A열 6번 이런식으로 row, col 정보가 있어야 할 것 같습니다
| Pointcut: join point에서 Aspect가 실행될 기준을 뜻한다. 스프링에서는 특정한 표현식 제공하여 Pointcut를 지정할 수 있다. | ||
| Advice: join point에서 aspect가 취하는 동작이다. | ||
|
|
||
| Proxy: 프록시는 대리인, 대리자의 뜻을 가지고있다. 앞서서 특정 로직이 수행되기 직전 또는 직후에 특정한 기능을 추가할 수 있다고했는데, 그래서 객체가 다른 객체를 호출할 때, 객체가 호출하는게 아니라 호출하는 객체와 똑같은 프록시 객체가 다른 객체를 호출하게된다. 그래서 프록시 객체에 추가적인 로직을 추가할 수 있는 것. |
There was a problem hiding this comment.
AOP에서 프록시 객체가 원본 객체를 대신해서 호출된다는 점을 잘 짚어주셨습니다!
스프링이 이 가짜 프록시 객체를 만들 때 인터페이스가 있는 경우와 없는 경우에 내부적으로 사용하는 기술이 다르다고 알고 있는데 최신 스프링 부트에서는 어떤 방식을 기본으로 채택하고 있는지, 그리고 그 이유는 무엇인지 같이 공부해보면 좋을 것 같아요!
관련 내용이 담긴 블로그 글 공유합니다!
https://giron.tistory.com/129
| 여기서 본격적으로 컴포넌트 스캔 과정에서 얻은 BeanDefinition을 토대로 Bean을 생성한다. BeanDefinition을 기반으로 Bean을 생성해 ApplicationContext에 등록하게된다. | ||
|
|
||
| ```java | ||
| //AbstractAutowireCapableBeanFactory 클래스 | ||
| @Override | ||
| protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object @Nullable [] args) throws BeanCreationException { | ||
| ... | ||
| try { | ||
| Object beanInstance = doCreateBean(beanName, mbdToUse, args); | ||
| ... | ||
| return beanInstance; | ||
| } | ||
| ... | ||
| } | ||
|
|
||
| ↓ | ||
|
|
||
| protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object @Nullable [] args) throws BeanCreationException { | ||
| ... | ||
| // Initialize the bean instance. | ||
| Object exposedObject = bean; | ||
| try { | ||
| populateBean(beanName, mbd, instanceWrapper); | ||
| ... | ||
| } | ||
| ... | ||
| } | ||
|
|
||
| //이 윗과정에서는 생성된 Bean 토대로 BeanWrapper 클래스를 만든 후에 넘긴다 | ||
|
|
||
| ↓ | ||
|
|
||
| protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) { | ||
| ... | ||
|
|
||
| if (hasInstantiationAwareBeanPostProcessors()) { | ||
| ... | ||
|
|
||
| for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) { | ||
| PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName); | ||
| ... | ||
| } | ||
| } | ||
|
|
||
| ... | ||
|
|
||
| if (needsDepCheck) { | ||
| PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); | ||
| checkDependencies(beanName, mbd, filteredPds, pvs); | ||
| } | ||
| ... | ||
| } | ||
| ``` |
There was a problem hiding this comment.
빈을 생성할 때 doCreateBean에서 populateBean으로 넘어가며 리플렉션을 통해 private 필드에도 접근하여 DI를 주입한다는 부분을 코드로 직접 확인하니 원리가 훨씬 더 잘 와닿았어요~ 정성스러운 정리 감사합니다!
| 추가로 스프링은 톰캣이라는 서버를 기본제공한다. 따라서 스프링을 동작하면 톰캣서버 위에서 웹서비스가 이루어진다. | ||
|
|
||
| 클라이언트가 요청을 보내면 톰캣서버로 보내지고, 톰캣서버는 몇 가지 필터들을 통해 요청을 필터링한 후, 스프링에게 요청을 넘긴다. 이 요청을 받는 역할을 DispatcherServlet이 수행하고, DispathcerServlet이 다시 Controller 측으로 데이터를 넘긴다. | ||
|
|
||
| 이렇게 클라이언트로부터 HTTP 요청을 받아서 간단한 정적 웹페이지를 반환하는 프로그램을 **서버**라고 칭한다. 하지만 데이터베이스 조회, 부가적인 로직 처리 등 정적 웹페이지가 아닌 더 복잡한 동적인 웹페이지를 반환해야할 때가 있는데, 이 때 이 부가적인 로직을 처리하는 미들웨어를 **WAS**(Web Application Server)라고 칭한다. 서버와는 다른 개념이다. | ||
|
|
||
| 스프링은 웹 서버의 구동을 더 쉽게 도와주는 프레임워크, 톰캣서버는 스프링을 구동할 수 있는 WAS, 아파치 서버는 요청을 받아서 WAS가 구동되도록하는 웹 서버에 해당한다. |
There was a problem hiding this comment.
웹 서버와 WAS의 역할을 아파치와 톰캣으로 비유해서 잘 정리해주셨네요!
덕분에 웹서버와 WAS의 차이를 잘 알 수 있었습니다 ㅎㅎ
| ```java | ||
| private Set<BeanDefinition> scanCandidateComponents(String basePackage) { | ||
| Set<BeanDefinition\> candidates = new LinkedHashSet<\>(); | ||
|
|
||
| try { | ||
| String packageSearchPattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + '/' + this.resourcePattern; | ||
| ... | ||
|
|
||
| Resource[] resources = getResourcePatternResolver().getResources(packageSearchPattern); | ||
|
|
||
| ... | ||
|
|
||
| for (Resource resource : resources) { | ||
| String filename = resource.getFilename(); | ||
| ... | ||
| try { | ||
| MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource); | ||
|
|
||
| if (isCandidateComponent(metadataReader)) { | ||
| ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); | ||
| sbd.setSource(resource); | ||
|
|
||
| if (isCandidateComponent(sbd)) { | ||
| ... | ||
| candidates.add(sbd); | ||
| } | ||
| else { | ||
| ... | ||
| } | ||
| } | ||
| } | ||
| ... | ||
| } | ||
| ... | ||
| } | ||
|
|
||
| return candidates; | ||
| } | ||
| ``` |
There was a problem hiding this comment.
스프링이 컴포넌트 스캔을 할 때 단순히 패키지 경로만 훑는 게 아니라, ResourcePatternResolver로 실제 .class 파일들을 다 찾아낸 다음 MetadataReader를 통해 바이트코드 메타데이터를 꼼꼼히 읽어들인다는 과정을 코드로 볼 수 있어서 좋았습니다! 정리하느라 수고 많으셨습니다!
| ```java | ||
| protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { | ||
| HttpServletRequest processedRequest = request; | ||
| ... | ||
| try { | ||
| ... | ||
|
|
||
| try { | ||
| ... | ||
|
|
||
| // Determine handler for the current request. | ||
| mappedHandler = getHandler(processedRequest); | ||
|
|
||
| ... | ||
|
|
||
| // Determine handler adapter and invoke the handler. | ||
| HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); | ||
| mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); | ||
|
|
||
| ... | ||
|
|
||
| finally { | ||
| ... | ||
| } | ||
| } | ||
| ``` |
There was a problem hiding this comment.
MVC 패턴에서 핸들링 매핑, 핸들러 어댑터 같은 용어들이 텍스트로만 보면 꽤 추상적으로 느껴져서 저도 코드로 3단계로 나눠서 보여줬는데, 이렇게 한 번에 나란히 보여주시니 구조가 더 잘 그려지는 것 같습니다!
정리하시느라 수고 많으셨습니다!!
No description provided.